package com.beantools.annotation.handler;

import com.beantools.annotation.NotNull;
import com.beantools.entity.EntityStorage;
import com.beantools.execption.HandlerException;
import com.beantools.filters.BuilderFilters;
import com.beantools.reflect.property.Property;
import com.beantools.util.ArrayUtil;
import com.beantools.util.CollectionUtil;
import com.beantools.util.valid.Validate;
import com.beantools.util.valid.ValidateFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

import static com.beantools.constants.AnnotationKeyword.*;
import static com.beantools.util.ObjectUtil.isNotNull;
import static com.beantools.util.ObjectUtil.isNull;

/**
 * 这是专门处理{@link NotNull}注解的<code>NotNullHandler</code>注解处理器。
 * 这里不允许用户通过构造器的显性地方式创建，比如以下代码所示：
 * <pre>
 *     <code>
 *         NotNullHandler notNullHandler=new NotNullHandler()
 *     </code>
 * </pre>
 * 而是通过{@link NotNullHandler#getInstance()}的方式动态创建对象，如以下代码所示：
 * <pre>
 *     <code>
 *          NotNullHandler notNullHandler=NotNullHandler.getInstance()
 *     </code>
 * </pre>
 * 这里主要验证调用者实例化的对象是否符合规则，比如以下的代码：
 * <pre>
 *     <code>
 *          Student student = new Student();
 *          student.setNo("1212");
 *          student.setId(11L);
 *          student.setStatus(PROJECT_STATUS_REVIEWED);
 *          NotNullHandler.getInstance().validate(student);
 *          System.out.println(student);
 *     </code>
 * </pre>
 *
 * @author baoya
 * @version 1.0
 * @since 1.1.0
 */

public class NotNullHandler<T> implements AnnotationHandler<T> {

    private String[] ignore;

    private String[] notNull;

    private boolean includingSupClass;

    private Class<? extends Annotation> annotationType;

    private NotNullHandler() {
    }

    public static NotNullHandler getInstance() {
        return new NotNullHandler();
    }


    /**
     * 这是接受一个对象的方法，通过对象<code>object</code>获取该对象类、属性、方法的注解。
     * 如果调用者使用的注解满足程序的需求，那么，直接返回true，否则，返回false。这里面分这几种情况:
     * <pre>
     *     <ol>
     *         <li>
     *             annotation NotNull keyword`s ignore is empty and notNull is empty and includingSupClass is false
     *         </li>
     *         <li>
     *              annotation NotNull keyword`s ignore is not empty and includingSupClass is false
     *         </li>
     *         <li>
     *              annotation NotNull keyword`s notNull is not Empty and includingSupClass is false
     *         </li>
     *          <li>
     *              annotation NotNull keyword`s ignore is empty and includingSupClass is false
     *         </li>
     *         <li>
     *              annotation NotNull keyword`s notNull is Empty and includingSupClass is false
     *         </li>
     *         <li>
     *              annotation NotNull keyword`s ignore is empty and includingSupClass is true
     *         </li>
     *          <li>
     *              annotation NotNull keyword`s ignore is empty and notNull`s ignore is null and includingSupClass is true
     *         </li>
     *          <li>
     *              annotation NotNull keyword`s notNull is empty and includingSupClass is true
     *         </li>
     *          <li>
     *              annotation NotNull keyword`s  ignore is not empty and includingSupClass is true
     *         </li>
     *         <li>
     *              annotation NotNull keyword`s  notNull is not empty and includingSupClass is true
     *         </li>
     *     </ol>
     *
     * </pre>
     *
     * @param object 泛型的实例化对象
     * @return true|false 如果呵护
     */
    @Override
    public void validate(T object) {
        if (isNull(object)) {
            throw new HandlerException("校验失败，对象为空");
        }
        Class clazz = object.getClass();
        //【1】类上面有注解
        Annotation var = clazz.getDeclaredAnnotation(NotNull.class);
        Property instance = Property.getInstance(clazz);
        if (isNotNull(var)) {
            validTargetType(object, clazz, var, instance);
        } else {
            Field[] fields = clazz.getDeclaredFields();
            for (Field declaredField : fields) {
                var = declaredField.getAnnotation(NotNull.class);
                if (isNull(var)) {
                    continue;
                }
                String fieldName = declaredField.getName();
                Object var7 = instance.getProperty(fieldName, object);
                if (isNull(var7)) {
                    throw new HandlerException("属性" + fieldName + "不能为空", clazz);
                }

            }
        }
    }

    /**
     * 这是接受一个对象数组的方法，如果对象<code>object</code>合乎调用者的要求，则直接返回true
     * 否则，返回false
     *
     * @param objects 泛型的实例化对象
     * @return true|false
     * @throws HandlerException
     */
    @Override
    public void validate(T[] objects) throws HandlerException {
        if (ArrayUtil.isEmpty(objects)) {
            throw new HandlerException("数组为空");
        }
        int index = 0;
        try {
            for (T object : objects) {
                index = getIndex(index, object);
            }
        } catch (HandlerException e) {
            throw new HandlerException(e.getMessage());
        }
    }


    /**
     * 这是接受一个对象容器的方法，如果对象<code>object</code>合乎调用者的要求，则直接返回true
     * 否则，返回false
     *
     * @param collection 泛型的实例化对象
     * @return true|false
     * @throws HandlerException
     */
    @Override
    public void validate(Collection<T> collection) throws HandlerException {
        if (CollectionUtil.isEmpty(collection)) {
            throw new HandlerException("容器为空");
        }
        int index = 0;
        try {
            for (Iterator iterator = collection.iterator(); iterator.hasNext(); ) {
                T t = (T) iterator.next();
                index = getIndex(index, t);
            }
        } catch (HandlerException e) {
            throw new HandlerException(e.getMessage());
        }
    }


    /**
     * 验证注解{@link NotNull}在实体类上的条件。
     *
     * @param object     当前实体类的对象
     * @param clazz      当前实体类的类型
     * @param annotation 实例化的注解
     * @param instance   实例化的属性对象
     */
    private void validTargetType(T object, Class clazz, Annotation annotation, Property instance) {
        init(annotation);
        if (ArrayUtil.isAllNotEmpty(ignore, notNull)) {
            throw new HandlerException("关键字" + IGNORE + "和关键字" + NOT_NULL + "若有，且只有一个", clazz);
        }
        try {
            if (ArrayUtil.isAllEmpty(ignore, notNull)) {
                validStorage(object, clazz, getStorage(clazz, includingSupClass));
            }
            if (ArrayUtil.isNotEmpty(ignore) && ArrayUtil.isEmpty(notNull)) {
                validIgnore(object, clazz, instance, getStorage(clazz, includingSupClass));
            }
            if (ArrayUtil.isEmpty(ignore) && ArrayUtil.isNotEmpty(notNull)) {
                validNotNull(object, clazz, instance, getStorage(clazz, includingSupClass));
            }
        } catch (HandlerException e) {
            throw new HandlerException(e.getMessage(), clazz);
        }
    }

    /**
     * 如果<code>includingSupClass =true</code>，那么，过滤出父类的<code>getter</code>方法，反之，
     * 只过滤出当前类的<code>getter</code>方法
     *
     * @param clazz             当前实例化对象的类型
     * @param includingSupClass 是否包含父类的布尔类型的参数
     * @return 实体类的仓库
     */
    private Set<EntityStorage> getStorage(Class clazz, boolean includingSupClass) {
        Method[] methods = includingSupClass ? clazz.getMethods() : clazz.getDeclaredMethods();
        return getStorage(clazz, methods);
    }

    /**
     * 这是验证{@link NotNullHandler#ignore}的方法。如果{@link NotNullHandler#ignore}不为空，但是
     * {@link NotNullHandler#notNull}为空的验证条件
     *
     * @param object     实体类对象
     * @param clazz      实体类对象的类型
     * @param instance   属性类的实例化对象
     * @param storageSet 实体类仓库
     */
    private void validIgnore(T object, Class clazz, Property instance, Set<EntityStorage> storageSet) {
        for (EntityStorage storage : storageSet) {
            boolean notEquals = true;
            String fieldName = storage.getPropertyName();
            for (String var1 : ignore) {
                if (var1.equalsIgnoreCase(fieldName)) {
                    notEquals = !notEquals;
                    break;
                }
            }
            nullThrowsException(object, clazz, instance, notEquals, fieldName);
        }
    }

    /**
     * 这是验证{@link NotNullHandler#ignore}的方法。如果{@link NotNullHandler#ignore}不为空，但是
     * {@link NotNullHandler#notNull}为空的验证条件
     *
     * @param object     实体类对象
     * @param clazz      实体类对象的类型
     * @param instance   属性类的实例化对象
     * @param storageSet 实体类仓库
     */
    private void validNotNull(T object, Class clazz, Property instance, Set<EntityStorage> storageSet) {
        for (EntityStorage storage : storageSet) {
            boolean equals = false;
            String fieldName = storage.getPropertyName();
            for (String var1 : notNull) {
                if (var1.equalsIgnoreCase(fieldName)) {
                    equals = true;
                    break;
                }
            }
            nullThrowsException(object, clazz, instance, equals, fieldName);
        }
    }

    /**
     * 如果在<code>equals=true</code>的情况下，而当前实例化的对象<code>object</code>的属性值为空，就会抛出
     * 这样的异常错误：<strong>属性+ fieldName + 不能为空</strong>
     *
     * @param object    实体类对象
     * @param clazz     实体类对象的类型
     * @param instance  属性类的实例化对象
     * @param equals    集合中的属性是否当前实例化对象的属性是否相等
     * @param fieldName 属性名称
     */
    private void nullThrowsException(T object, Class clazz, Property instance, boolean equals, String fieldName) {
        if (equals) {
            Object var2 = instance.getProperty(fieldName, object);
            if (isNull(var2)) {
                throw new HandlerException("属性" + fieldName + "不能为空", clazz);
            }
        }
    }

    /**
     * 这是验证实体仓库为空的方法。如果在某个条件下，实体对象的某个属性为空，则会报出<strong>属性值不能为空</strong>
     * 异常。
     *
     * @param object     实体类的对象
     * @param clazz      实体类的类型
     * @param storageSet 实体类的方法和属性的仓库
     */
    private void validStorage(T object, Class clazz, Set<EntityStorage> storageSet) {
        for (EntityStorage storage : storageSet) {
            Object var1;
            try {
                var1 = storage.getMethod().invoke(object);
                if (isNull(var1)) {
                    throw new HandlerException("属性" + storage.getPropertyName() + "值不能为空", clazz);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 这是获取实体类<code>EntityStorage</code>实例化对象的容器。参数<code>clazz</code>在这里没有实际意义
     * 只是在程序抛出异常时，用于告知调用者是哪个实体类出现了异常。
     * 参数<code>methods</code>是实体类的方法集合，内部调用{@link com.beantools.filters.MethodFilters#filterByGetter(Method)}
     * 方法来过滤出当前实例化对象的<code>getter</code>方法
     *
     * @param clazz   这是待校验对象的类型
     * @param methods 带过滤的当前类对象的所有方法
     */
    private Set<EntityStorage> getStorage(Class clazz, Method[] methods) {
        if (ArrayUtil.isEmpty(methods)) {
            throw new HandlerException("没有对应的get或is方法", clazz);
        }
        return BuilderFilters.builderMethodFilters().filterByGetter(methods);
    }


    /**
     * 初始化私有属性的方法,比如初始化<code>ignore</code>数组
     *
     * @param annotation 这是<code>java.lang.Annotation</code>的注解实例化对象
     */
    private void init(Annotation annotation) {
        annotationType = annotation.annotationType();
        Object var2 = getKeywordValue(annotation, annotationType, IGNORE);
        Object var3 = getKeywordValue(annotation, annotationType, INCLUDING_SUP_CLASS);
        Object var4 = getKeywordValue(annotation, annotationType, NOT_NULL);
        ignore = getStrings(var2);
        notNull = getStrings(var4);
        includingSupClass = isBoolean(var3);
    }

    /**
     * 这是将<code>Object</code>对象转为boolean对象的方法。如果<code>var1</code>是布尔类型的<code>Boolean</code>
     * 程序将其转化为布尔对象。
     *
     * @param var1 待转为布尔类型的对象
     * @return 如果<code>var1</code>是布尔类型的实例化对象，其可以变转化为布尔类型的对象，否则，
     * 返回<code>false</code>
     */
    private boolean isBoolean(Object var1) {
        boolean var2 = false;
        if (var1 instanceof Boolean) {
            var2 = (boolean) var1;
        }
        return var2;
    }

    /**
     * 这是将<code>Object</code>对象转为字符串数组的方法。如果<code>var1</code>是字符串<code>String[]</code>
     * 程序将其转化为字符串数组对象。同时，去除空字符串或<code>null</code>字符串，获取真正的数组字符串
     *
     * @param var1 待转为字符串数组的对象
     * @return 如果<code>var1</code>是字符串数组的实例化对象，其可以变转化为字符串数组对象，否则，
     * 返回<code>null</code>
     */
    private String[] getStrings(Object var1) {
        String[] var2 = null;
        if (var1 instanceof String[]) {
            String[] var3 = (String[]) var1;
            var2 = ArrayUtil.removeNull(var3, String.class);
        }
        return var2;
    }


    /**
     * 获取注解{@link NotNull}的关键字的值
     *
     * @param var1 注解{@link NotNull}
     * @param var2 注解的类型，比如{@link NotNull#ignore()}
     * @param var3 注解的关键字的名字，比如<code>ignore</code>
     * @return 返回注解的值
     */

    private Object getKeywordValue(Annotation var1, Class<? extends Annotation> var2, String var3) {
        try {
            Method ignore = var2.getDeclaredMethod(var3);
            return ignore.invoke(var1, null);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 这里判断{@link NotNull}注解关键字{@link NotNull#ignore()}中忽略的属性是否是实体类中的属性
     * 如果不是，就会抛出{@link com.beantools.execption.HandlerException}异常
     *
     * @param var1  这是实体仓库的容器，容器中包含当前实例化对象的属性
     * @param clazz 当前实例化对象的类
     * @param var2  关键字{@link NotNull#ignore()}中忽略的属性
     * @return true|false 如果注解<code>methods</code>的方法名都在<code>keywords</code>,则返回true
     * 否则，返回false
     * @throws HandlerException 处理器异常信息，比如类com.beantools.testpo.Student的异常信息: 不存在“ccccc”这样的属性
     */
    @Deprecated
    public boolean validate(Field[] var1, Class clazz, String... var2) throws HandlerException {
        return Validate.validate(new ValidateFactory() {
            @Override
            public boolean validate(Object... objects) {
                if (ArrayUtil.isNotEmpty(var1)) {
                    for (String var3 : var2) {
                        boolean var4 = false;
                        for (Field field : var1) {
                            String fieldName = field.getName();
                            if (var3.equalsIgnoreCase(fieldName)) {
                                var4 = true;
                                break;
                            }
                        }
                        if (!var4) {
                            throw new HandlerException("不存在“" + var3 + "”这样的属性", clazz);
                        }
                    }
                }
                return true;
            }
        }, var1, var2);
    }

    /**
     * 这里判断{@link NotNull}注解关键字{@link NotNull#ignore()}中忽略的属性是否是实体类中的属性
     * 如果不是，就会抛出{@link com.beantools.execption.HandlerException}异常
     *
     * @param var1  这是实体仓库的容器，容器中包含当前实例化对象的属性
     * @param clazz 当前实例化对象的类
     * @param var2  关键字{@link NotNull#ignore()}中忽略的属性
     * @return true|false 如果注解<code>methods</code>的方法名都在<code>keywords</code>,则返回true
     * 否则，返回false
     * @throws HandlerException 处理器异常信息，比如类com.beantools.testpo.Student的异常信息: 不存在“ccccc”这样的属性
     */
    @Deprecated
    public boolean validate(Set<EntityStorage> var1, Class clazz, String... var2) throws HandlerException {
        return Validate.validate(new ValidateFactory() {
            @Override
            public boolean validate(Object... objects) {
                if (CollectionUtil.isEmpty(var1)) {
                    if (CollectionUtil.isEmpty(var1)) {
                        throw new HandlerException("没有get方法", clazz);
                    }
                }
                for (String var3 : var2) {
                    boolean var4 = false;
                    for (EntityStorage storage : var1) {
                        String fieldName = storage.getPropertyName();
                        if (var3.equalsIgnoreCase(fieldName)) {
                            var4 = true;
                            break;
                        }
                    }
                    if (!var4) {
                        throw new HandlerException("不存在“" + var3 + "”这样的属性", clazz);
                    }
                }
                return true;
            }
        }, var1, var2);
    }

    /**
     * 当调用者调用{@link this#validate(Object[])} 或者调用{@link this#validate(Collection)}这个方法时，
     * 内部会进入到当前方法中，其用来判定某个对象是否为空。
     *
     * @param index  容器或者数组的下标
     * @param object 容器或数组遍历到的当前对象
     * @return 如果校验成功，会返回容器或数组的下标，否则，直接抛出异常
     * @throws HandlerException 第几对象校验失败，失败原因
     */
    private int getIndex(int index, T object) throws HandlerException {
        try {
            index++;
            validate(object);
        } catch (HandlerException e) {
            throw new HandlerException("第" + index + "对象校验失败，失败原因:" + e.getMessage());
        }
        return index;
    }
}
